package tinsiec;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.concurrent.BlockingQueue;

/**
 * klasa odpowiedzialna za obsluge
 * polaczen bezposrednich, pomiedzy uzytkownikami
 * umozliwia wysylanie oraz odbieranie wiadomosci
 * od konkretnych klientow
 * testuje jedynie kompletnosc odbieranych wiadomosci,
 * bez sprawdzania nadawcy
 *
 * @author Marek Duda
 */
public class TCPModule implements Runnable{

    private ServerSocket server;  // socket nasluchujacy
    private int port;           // port na ktorym nasluchuje
    private boolean aktiv = true;  // is sending/listening running
    private boolean running = false;

    private BlockingQueue<Message> reciveQueue; // wiadomosci odebrane
    private BlockingQueue<Message> sendQueue;    // wiadomosci do wyslania
    private XMLModule xml;                      // modul xml do testowania wiadomosci
    private Thread sender;      // oddzielny watek do wysylania wiadomosci
    private Thread reciver;     // watek do odbierania wiadomosci

    /**
     * konstruktor klasy przyjmyjacy za argument
     * nr portu, na którym bedzie realizowane nasluchiwanie
     * @param port port na ktorym bedzie prowadzone nasluchiwanie
     */
    public TCPModule(int port){

        this.port = port;
        xml = new XMLModule();
        if(!running){
            try {
                server = new ServerSocket(port);
            } catch (IOException ex) {
                running = true;
                System.out.println("unable to start server: "+ex);
            }
        }
    }

    /**
     * bezargumentowy konstruktor
     * domyslne nasluchiwanie na porcie 55555
     */
    public TCPModule(){
        this(55555);
    }

    /**
     * umozliwia uzyskanie nr portu,
     * na ktorym jest prowadzone nasluchiwanie
     * wiadomosci przychodzacych
     * @return nr portu na ktorym prowadzone jest nasluchiwanie
     */
    public int getPort(){
        return port;
    }

    /**
     * przekazuje referancje do kolejki waidomosci przychodzacych
     * @param queue BlockingQueue<Message>
     */
    public void setRecivQueue(BlockingQueue<Message> queue){
        reciveQueue = queue;
    }

    /**
     * zwraca referencje do kolejki wiadomosci przychodzacych
     * @return BlockingQueue<Message> kolejka wiadomosci
     */
    public BlockingQueue<Message> getReciveQueue(){
        return reciveQueue;
    }

    /**
     * ustawia kolejke wiadomosci wysylanych
     * @param queue BlockingQueue<Message> kolejka wiadomosci
     */
    public void setSendQueue(BlockingQueue<Message> queue){
        sendQueue = queue;
    }

    /**
     * zwraca referencje do kolejki wiadomosci wysylanych
     * @return referencja do kolejki wiadomosci
     */
    public BlockingQueue<Message> getSendQueue(){
        return sendQueue;
    }

    /**
     * tworzy watek wysylajacy wiadomosci
     * oraz watek odbierajacy
     * i rozpoczyna ich dzialanie
     */
    public void start(){
        aktiv = true;
        reciver = new Thread(this); // tworzy watek odbierajacy
        sender = new Thread(new Runnable() // tworzy watek wysylajacy
        {
            Message msg;
            public void run() {
                boolean check = true; // czy ostatnia wiadomosc wyslana
                String m = "";  // odebrany tekst
                int i = 0;
                int times = 6; // ilosc prob wyslania wiadomosci
                while(aktiv){
                    System.out.println("wiadomosc nr: "+ i);
                    try {
                        times--; // zmniejszamy ilosc prob wyslania
                        if(check || (times < 0)){ // jesli ostatnia zostala wyslana bierz kolejna
                            msg = sendQueue.take();
                            m = xml.parseMessage(msg.getMessage());
                            times = 6;
                            System.out.println("wysyla: " + m);
                        }
                        // jesli nie to proba wyslania jeszcze raz
                        // and send
                        try {
                            Socket ss = new Socket(msg.getIP(), port);
                            DataOutputStream dos = new DataOutputStream(ss.getOutputStream());
                            dos.writeUTF(m);
                            dos.close();
                            ss.close();
                            System.out.println("wyslal: " + m);
                            m = "";
                            check = true; // jesli wyslana to ok
                            
                        } catch (UnknownHostException ex) {
                            check = false;
                            System.out.println("unknown host:  " + ex);
                        } catch (IOException ex) {
                            check = false;
                            System.out.println("unable to send:  " +ex);
                        }
                    } catch (InterruptedException ex) {
                        System.out.println("unable to get message:  "+ex);
                    }
                    i++;
                } //while
            }
        });
        
        reciver.start();
        sender.start();
    }

    /**
     * zatrzymuje watek wysylajacy i odbierajacy
     */
    public void stop(){
        running = false;
        aktiv = false;
        if(sender != null){
           sender.interrupt();
        }
        if(reciver != null){
           reciver.interrupt();
        }
    }

    /**
     * nasluchuje przychodzace polaczenia
     * i odbiera od nich wiadomosci
     */
    public void run() {
        
        String ip, text = "";
        Socket ss;

        while(aktiv){  // recive messages
            System.out.println("start");
            if(!running){
                StringBuilder buffer = new StringBuilder();
                try {
                    ss = server.accept();
                    System.out.println("zaakceptowal_odbiranie");
                    ip = (ss.getInetAddress()).toString();
                    ip = ip.replaceFirst("/", "");
                    
                    DataInputStream dis = new DataInputStream(ss.getInputStream());

                    while(!xml.checkMessage(text)){
                        buffer.append(dis.readUTF());
                        text += buffer.toString();
                    }

                    text = xml.unparseMessage(text);
                    System.out.println("odebral wiadomosc: "+ text);
                    Message msg = new Message(ip, text);
                    if(reciveQueue != null){
                        reciveQueue.add(msg);
                        System.out.println("wrzucil do kolejki odebranych");
                    }
                    buffer.delete(0, buffer.length());
                    dis.close();
                    ip = text = "";
                    ss.close();
                } catch (IOException ex) {
                    System.out.println("blad "+ex);
                }
            }
        }
    }

}
